package org.slusk.thynwor.feedback;

import java.util.LinkedList;

import org.slusk.thynwor.corestructures.Bot;
import org.slusk.thynwor.corestructures.Drone;
import org.slusk.thynwor.corestructures.Entity;
import org.slusk.thynwor.corestructures.Environment;
import org.slusk.thynwor.corestructures.Food;
import org.slusk.thynwor.corestructures.GridMap;
import org.slusk.thynwor.corestructures.Obstacle;
import org.slusk.thynwor.corestructures.Region;
import org.slusk.thynwor.corestructures.Drone.MoveOrder;
import org.slusk.thynwor.corestructures.Region.RegionType;
import org.slusk.thynwor.util.Coordinates;
import org.slusk.thynwor.util.Direction;

import com.anji.hyperneat.nd.NDFloatArray;
import com.anji.hyperneat.onlinereinforcement.trainingbank.Feedback;


public abstract class AbstractFeedbackProvider implements IFeedbackProvider {

	protected Environment environment;
	
	public AbstractFeedbackProvider(Environment environment) {
		this.environment = environment;
	}
	
	protected GridMap map() {return environment.getMap();}


	/* (non-Javadoc)
	 * @see org.slusk.thynwor.corestructures.IFeedbackProvider#getFeedback(org.slusk.thynwor.corestructures.Drone.MoveOrder, com.anji.hyperneat.nd.NDFloatArray, org.slusk.thynwor.util.Coordinates, com.anji.hyperneat.nd.NDFloatArray)
	 */
	@Override
	public abstract Feedback getFeedback(Drone bot, MoveOrder mo, NDFloatArray inputs, Coordinates outputCoords, NDFloatArray outputs); 

	
	/* (non-Javadoc)
	 * @see org.slusk.thynwor.corestructures.IFeedbackProvider#provideFitnessFeedBackToEnvironment(org.slusk.thynwor.corestructures.Drone.MoveOrder)
	 */
	@Override
	public void provideFitnessFeedBackToEnvironment(Drone bot, float[] signalInputs, MoveOrder mo) {
		
		if (null == mo) {
			// No big deal, this might be ok
		} else if (map().isFree(mo.proposedCoordinates)) {
			// Cool.  Now see if it was useful
			
			// If it's towing, it ought be moving toward a goal
			if (bot.isTowingResource()) {
				// Just one goal for now
				Region r = map().getRegionsOfType(RegionType.BASE).getFirst();
				Coordinates coords = bot.getCoordinates();
				LinkedList<Direction> okdirs = map().getSimilarAvailableDirections(coords, GridMap.coordinatesToDirection(coords, r.getCenter()));
				// Reinforce moving in general direction of goal.
				if (okdirs.contains(mo.proposedDirection)) {
					environment.incrementMoveFoodTowardGoal();
				}
			} else {
				// If it chose a relatively strong signal, that was a good choice
				if (signalInputs[mo.proposedDirection.ordinal()] > 0.4f) {
					environment.incrementMoveTowardSignal();
				}
			}
			
		} else if (map().isInBounds(mo.proposedCoordinates)) {
			// Somethings in the way. Let's see what
			Entity e = map().get(mo.proposedCoordinates).getContents();
			if (e instanceof Obstacle) {
				environment.incrementHitObstacle();
			} else if (null == bot.getInteractee()) {
				if (e instanceof Bot) {
					// Penalty
					environment.incrementHitObstacle();
				} else if (e instanceof Food) {
					if (e.getSignal() != null) {
						// A very good choice
						environment.incrementAssistWithFood();
					} else {
						// An ok choice
						environment.incrementAttachedToFood();
					}
				}
			} else {
				// Already had a resource but bumped another.  Penalty!
				environment.incrementHandsFull();
			}
		} else {
			// Ran into wall. Penalty!
			environment.incrementHitObstacle();
		}			
	}

	protected Coordinates getMaxFromInputDim(NDFloatArray inputs, int zindex/*, int sindex*/) {
		Coordinates bestCoords = null;
		float current, best = -1;
		
		for (int y = 0; y < 3; y++) {
			for (int x = 0; x < 3; x++) {
				current = inputs.get(x,y, zindex/*, sindex*/);
				if (bestCoords == null || current > best) {
					best = current;
					bestCoords = new Coordinates(x, y);
				}
			}
		}
		
		return bestCoords;
	}
}
